package com.quectel.emp.controller.scheduling;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.common.util.sys.SysCodeUtils;
import com.quectel.constant.core.scheduling.SchedulingApplyConstants;
import com.quectel.constant.core.scheduling.SchedulingAttendanceDailyConstants;
import com.quectel.constant.core.scheduling.SchedulingAttendanceGroupConstants;
import com.quectel.constant.core.scheduling.SchedulingWorkOvertimeConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.sys.SysCodeKeyConstants;
import com.quectel.core.module.dept.dto.DeptPersonDto;
import com.quectel.core.module.scheduling.dto.*;
import com.quectel.core.module.scheduling.dto.mongo.MongoOriginalCheckRecordDto;
import com.quectel.core.module.scheduling.service.*;
import com.quectel.core.module.scheduling.service.mongo.MongoOriginalCheckRecordService;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyLeaveRecordDto;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyOutRecordDto;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyResumptionLeaveRecordDto;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyTripRecordDto;
import com.quectel.core.module.schedulingapply.service.*;
import com.quectel.emp.controller.BaseController;
import com.quectel.emp.util.SessionHolder;
import com.quectel.emp.vo.scheduling.*;
import com.quectel.util.common.*;
import com.quectel.util.kit.PageFetchKit;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *
 * @author ryan
 * @email ryan.ran@quectel.com
 * @date 2023/07/13 18:58
 */
@RestController
@RequestMapping("schedulingAttendanceDaily")
@Tag(name = "SchedulingAttendanceDailyController", description = "考勤相关 API")
public class SchedulingAttendanceDailyController extends BaseController {

    private static final String PERSON_ATTENDANCE_INFO_CACHE = "PERSON_ATTENDANCE_INFO_CACHE_KEY:";

    @DubboReference
    private SchedulingAttendanceDailyService schedulingAttendanceDailyService;
    @DubboReference
    private SchedulingAttendanceGroupPersonBindingService schedulingAttendanceGroupPersonBindingService;
    @DubboReference
    private SchedulingAttendanceGroupHeadBindingService schedulingAttendanceGroupHeadBindingService;
    @DubboReference
    private SchedulingAttendanceDailyTimeService schedulingAttendanceDailyTimeService;
    @DubboReference
    private SchedulingAttendanceGroupPersonWorkTimeConfigService schedulingAttendanceGroupPersonWorkTimeConfigService;
    @DubboReference
    private SchedulingApplyRecordService schedulingApplyRecordService;
    @DubboReference
    private SchedulingApplyResumptionLeaveRecordService schedulingApplyResumptionLeaveRecordService;
    @DubboReference
    private SchedulingApplyLeaveRecordService schedulingApplyLeaveRecordService;
    @DubboReference
    private SchedulingApplyTripRecordService schedulingApplyTripRecordService;
    @DubboReference
    private SchedulingApplyOutRecordService schedulingApplyOutRecordService;
    @DubboReference
    private SchedulingWorkOvertimeConfigService schedulingWorkOvertimeConfigService;
    @DubboReference
    private SchedulingAttendanceDailyApplyService schedulingAttendanceDailyApplyService;
    @DubboReference
    private MongoOriginalCheckRecordService mongoOriginalCheckRecordService;
    @DubboReference
    private SchedulingAttendanceMonthService schedulingAttendanceMonthService;

    @GetMapping("statistics")
    @Operation(summary = "考勤组统计")
    public Response<GroupAttendanceStatisticsResp> statistics(
            @Parameter(description = "考勤组id", required = true) @RequestParam Long groupId,
            @Parameter(description = "时间类型 1 日 2 周 3 月", required = true) @RequestParam Byte timeType,
            @Parameter(description = "时间范围 日 yyyy-MM-dd 周 yyyy-MM-dd~yyyy-MM-dd 月 yyyy-MM", required = true) @RequestParam String time
    ) {
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

        List<DeptPersonDto> deptPersonDtos = schedulingAttendanceGroupHeadBindingService.selectPersonByGroupId(groupId);
        boolean b = deptPersonDtos.parallelStream().noneMatch(o -> o.getId().equals(deptPersonDto.getId()));
        if (b) {
            return Response.error("非该考勤组管理员");
        }


        Map<String, Object> params = new HashMap<>();
        params.put(LambdaUtil.getFieldName(SchedulingAttendanceDailyDto::getSchedulingAttendanceGroupId), groupId);
        if (timeType.equals((byte) 1)) {
            Date calDay = DateUtils.parse(time, DateUtils.FormatType.SIMPLE);
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfDay(calDay), DateUtils.FormatType.COMMON));
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfDay(calDay), DateUtils.FormatType.COMMON));
        } else if (timeType.equals((byte) 2)) {
            String[] split = time.split("~");
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfDay(DateUtils.parse(split[0], DateUtils.FormatType.SIMPLE)), DateUtils.FormatType.COMMON));
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfDay(DateUtils.parse(split[1], DateUtils.FormatType.SIMPLE)), DateUtils.FormatType.COMMON));
        } else if (timeType.equals((byte) 3)) {
            Date calDay = DateUtils.parse(time, DateUtils.FormatType.MONTH);
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfMonth(calDay), DateUtils.FormatType.COMMON));
            params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfMonth(calDay), DateUtils.FormatType.COMMON));
        } else {
            return Response.error("时间类型错误");
        }
        List<SchedulingAttendanceDailyDto> schedulingAttendanceDailyDtos = PageFetchKit.fetchAll(req -> schedulingAttendanceDailyService.queryList(req), params, 500);

        GroupAttendanceStatisticsResp groupAttendanceStatisticsResp = new GroupAttendanceStatisticsResp();

//        BigDecimal workOvertimeHour = BigDecimal.ZERO;
//        BigDecimal leaveHour = BigDecimal.ZERO;
//        BigDecimal tripHour = BigDecimal.ZERO;
//        BigDecimal outHour = BigDecimal.ZERO;
//        BigDecimal attendanceHour = BigDecimal.ZERO;
//        BigDecimal realityAttendanceHour = BigDecimal.ZERO;
        int workOvertimeCount = 0;
        int leaveCount = 0;
        int tripCount = 0;
        int outCount = 0;

        int attendanceDay = 0;
        int realityAttendanceDay = 0;
        int lakeCount = 0;
        int lateCount = 0;
        int earlyCount = 0;
        int normalCount = 0;

        Map<Long, GroupAttendanceStatisticsResp.AbnormalPersonCls> abnormalPersonClsMap = new HashMap<>();
        Map<Long, DeptPersonDto> personMap = new HashMap<>();
        for (SchedulingAttendanceDailyDto attendanceDailyDto : schedulingAttendanceDailyDtos) {
            personMap.putIfAbsent(attendanceDailyDto.getPersonId(), attendanceDailyDto.getDeptPerson());
//            workOvertimeHour = workOvertimeHour.add(Objects.nonNull(attendanceDailyDto.getWorkOvertimeHour()) ? attendanceDailyDto.getWorkOvertimeHour() : BigDecimal.ZERO);
//            leaveHour = leaveHour.add(Objects.nonNull(attendanceDailyDto.getLeaveHour()) ? attendanceDailyDto.getLeaveHour() : BigDecimal.ZERO);
//            tripHour = tripHour.add(Objects.nonNull(attendanceDailyDto.getTripHour()) ? attendanceDailyDto.getTripHour() : BigDecimal.ZERO);
//            outHour = outHour.add(Objects.nonNull(attendanceDailyDto.getOutHour()) ? attendanceDailyDto.getOutHour() : BigDecimal.ZERO);
//            attendanceHour = attendanceHour.add(Objects.nonNull(attendanceDailyDto.getAttendanceHour()) ? attendanceDailyDto.getAttendanceHour() : BigDecimal.ZERO);
//            realityAttendanceHour = realityAttendanceHour.add(Objects.nonNull(attendanceDailyDto.getRealityAttendanceHour()) ? attendanceDailyDto.getRealityAttendanceHour() : BigDecimal.ZERO);
            if (attendanceDailyDto.getWorkOvertimeHour() != null && BigDecimal.ZERO.compareTo(attendanceDailyDto.getWorkOvertimeHour()) < 0) {
                workOvertimeCount++;
            }
            if (CollectionUtils.isNotEmpty(attendanceDailyDto.getApplies())) {
                for (SchedulingAttendanceDailyApplyDto apply : attendanceDailyDto.getApplies()) {
                    if (SchedulingApplyConstants.ApplyTypeEnum.APPLY_LEAVE.getType().equals(apply.getApplyType())) {
                        leaveCount++;
                    } else if (SchedulingApplyConstants.ApplyTypeEnum.OUT.getType().equals(apply.getApplyType())) {
                        outCount++;
                    } else if (SchedulingApplyConstants.ApplyTypeEnum.TRIP.getType().equals(apply.getApplyType())) {
                        tripCount++;
                    }
                }
            }
            if (SchedulingAttendanceDailyConstants.SituationTypeEnum.WORK.getType().equals(attendanceDailyDto.getSituation())
                    && CollectionUtils.isNotEmpty(attendanceDailyDto.getTimes())) {
                attendanceDay++;

                int personLakeCount = 0;
                int personLateCount = 0;
                int personEarlyCount = 0;
                int personNormalCount = 0;
                for (int i = 0; i < attendanceDailyDto.getTimes().size(); i++) {
                    if (SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.NORMAL.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckWorkStatus())
                            || SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.NORMAL.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckClosingStatus())) {
                        normalCount++;
                        personNormalCount++;
                    }
                    if (SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LATE.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckWorkStatus())
                            || SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LATE.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckClosingStatus())) {
                        lateCount++;
                        personLateCount++;
                    }
                    if (SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.EARLY.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckWorkStatus())
                            || SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.EARLY.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckClosingStatus())) {
                        earlyCount++;
                        personEarlyCount++;
                    }
                    if (SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckWorkStatus())
                            || SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(attendanceDailyDto.getTimes().get(0).getCheckClosingStatus())) {
                        lakeCount++;
                        personLakeCount++;
                    }
                }
                if (personLakeCount == 0) {
                    // 有缺卡
                    realityAttendanceDay++;
                }

                int finalPersonEarlyCount = personEarlyCount;
                int finalPersonLakeCount = personLakeCount;
                int finalPersonNormalCount = personNormalCount;
                int finalPersonLateCount = personLateCount;
                GroupAttendanceStatisticsResp.AbnormalPersonCls abnormalPerson = abnormalPersonClsMap.computeIfAbsent(attendanceDailyDto.getPersonId(), personId -> {
                    GroupAttendanceStatisticsResp.AbnormalPersonCls abnormalPersonCls = new GroupAttendanceStatisticsResp.AbnormalPersonCls();
                    abnormalPersonCls.setPersonId(personId);

                    DeptPersonDto p = personMap.get(personId);
                    if (p != null) {
                        abnormalPersonCls.setPersonJobNo(p.getJobNo());
                        abnormalPersonCls.setPersonName(p.getName());
                        abnormalPersonCls.setPersonFaceImg(p.getFaceImg());
                    }
                    return abnormalPersonCls;
                });

                abnormalPerson.setEarlyCount(finalPersonEarlyCount);
                abnormalPerson.setLateCount(finalPersonLateCount);
                abnormalPerson.setLakeCount(finalPersonLakeCount);
                abnormalPerson.setNormalCount(finalPersonNormalCount);
                int total = finalPersonEarlyCount + finalPersonLateCount + finalPersonLakeCount + finalPersonNormalCount;
                abnormalPerson.setAbnormalRate(total == 0 ? BigDecimal.ZERO : new BigDecimal(String.valueOf((finalPersonEarlyCount + finalPersonLateCount + finalPersonLakeCount) * 100))
                        .divide(new BigDecimal(total), 2, RoundingMode.HALF_UP));
                abnormalPersonClsMap.put(abnormalPerson.getPersonId(), abnormalPerson);
            }
        }

        groupAttendanceStatisticsResp.setLeavePersonCount(leaveCount);
        groupAttendanceStatisticsResp.setTripPersonCount(tripCount);
        groupAttendanceStatisticsResp.setOutPersonCount(outCount);
        groupAttendanceStatisticsResp.setWorkOvertimePersonCount(workOvertimeCount);
        groupAttendanceStatisticsResp.setGroupLakeCount(lakeCount);
        groupAttendanceStatisticsResp.setGroupLateCount(lateCount);
        groupAttendanceStatisticsResp.setGroupEarlyCount(earlyCount);
        groupAttendanceStatisticsResp.setGroupNormalCount(normalCount);
        groupAttendanceStatisticsResp.setAttendanceDay(attendanceDay);
        groupAttendanceStatisticsResp.setRealityAttendanceDay(realityAttendanceDay);
        groupAttendanceStatisticsResp.setAttendanceRate(attendanceDay == 0 ? BigDecimal.ZERO
                : new BigDecimal((100 * realityAttendanceDay) + "").divide(new BigDecimal(attendanceDay + ""), 1, RoundingMode.HALF_UP));
        Collection<GroupAttendanceStatisticsResp.AbnormalPersonCls> values = abnormalPersonClsMap.values();
        List<GroupAttendanceStatisticsResp.AbnormalPersonCls> collect = values.stream()
                .filter(o -> (o.getEarlyCount() + o.getLateCount() + o.getLakeCount()) > 0)
                .sorted(Comparator.comparingInt(a -> -(a.getEarlyCount() + a.getLateCount() + a.getLakeCount())))
                .collect(Collectors.toList());
        groupAttendanceStatisticsResp.setAbnormalPersons(collect);

        return Response.<GroupAttendanceStatisticsResp>ok().wrap(groupAttendanceStatisticsResp);
    }

    @GetMapping("summary")
    @Operation(summary = "月度汇总")
    public Response<SchedulingAttendanceMonthDto> summary(
            @Parameter(description = "月份 yyyy-MM", required = true) @RequestParam String month
    ){
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

        SchedulingAttendanceMonthDto schedulingAttendanceMonthDto = schedulingAttendanceMonthService.selectOne(deptPersonDto.getId(), month);
        if (schedulingAttendanceMonthDto == null) {
            schedulingAttendanceMonthDto = new SchedulingAttendanceMonthDto();
        } else if(schedulingAttendanceMonthDto.getAttendanceDay() != null && schedulingAttendanceMonthDto.getRealityAttendanceDay() != null) {
            schedulingAttendanceMonthDto.setUnattendedDay(schedulingAttendanceMonthDto.getAttendanceDay() - schedulingAttendanceMonthDto.getRealityAttendanceDay());
        }
        return Response.<SchedulingAttendanceMonthDto>ok().wrap(schedulingAttendanceMonthDto);
    }

    @GetMapping("checkInfo")
    @Operation(summary = "考勤信息")
    public Response<DailyCheckInfoResp> checkInfo(
            @Parameter(description = "日期 yyyy-MM-dd", required = true) @RequestParam String date
    ) {
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

        SchedulingAttendanceDailyDto attendanceDailyDto = schedulingAttendanceDailyService.selectOne(deptPersonDto.getId(), date);
        if (Objects.isNull(attendanceDailyDto)) {
            return Response.error("当天没有考勤");
        }

        final Date currentDate = new Date();

        DailyCheckInfoResp dailyCheckInfoResp = new DailyCheckInfoResp();

        dailyCheckInfoResp.setChecks(Lists.newArrayList());
        if (CollectionUtils.isNotEmpty(attendanceDailyDto.getTimes())) {
            List<DailyPreCheckResp.CheckTimeCls> checkTimes = attendanceDailyDto.getTimes().parallelStream().map(o -> {
                        DailyPreCheckResp.CheckTimeCls checkTimeCls = new DailyPreCheckResp.CheckTimeCls();

                        checkTimeCls.setSchedulingName(o.getName());

                        DailyPreCheckResp.CheckTimeItemCls workItem = StringUtils.isNotBlank(o.getCheckWorkFieldJson())
                                ? JacksonUtils.parse(o.getCheckWorkFieldJson(), DailyPreCheckResp.CheckTimeItemCls.class)
                                : new DailyPreCheckResp.CheckTimeItemCls();
                        workItem.setTime(o.getWorkTime());
                        workItem.setCheckTime(o.getCheckWorkTime());
                        workItem.setCheckStatus(DateUtil.compare(currentDate, o.getMissingWorkTime()) < 0
                                && SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(o.getCheckWorkStatus())
                                ? null : o.getCheckWorkStatus());
                        workItem.setCheckIsField(o.getCheckWorkIsField());
                        workItem.setCheckIsRepair(o.getCheckWorkIsRepair());
                        workItem.setCheckAddress(o.getCheckWorkAddress());
                        checkTimeCls.setWorkItem(workItem);

                        DailyPreCheckResp.CheckTimeItemCls closingItem = StringUtils.isNotBlank(o.getCheckClosingFieldJson())
                                ? JacksonUtils.parse(o.getCheckClosingFieldJson(), DailyPreCheckResp.CheckTimeItemCls.class)
                                : new DailyPreCheckResp.CheckTimeItemCls();
                        closingItem.setTime(o.getClosingTime());
                        closingItem.setCheckTime(o.getCheckClosingTime());
                        closingItem.setCheckStatus(DateUtil.compare(currentDate, o.getEndSignTime()) < 0
                                && SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(o.getCheckClosingStatus())
                                ? null : o.getCheckClosingStatus());
                        closingItem.setCheckIsField(o.getCheckClosingIsField());
                        closingItem.setCheckIsRepair(o.getCheckClosingIsRepair());
                        closingItem.setCheckAddress(o.getCheckClosingAddress());
                        checkTimeCls.setClosingItem(closingItem);

                        return checkTimeCls;
                    }).sorted((a, b) -> DateUtil.compare(a.getWorkItem().getTime(), b.getWorkItem().getTime()))
                    .collect(Collectors.toList());
            dailyCheckInfoResp.setChecks(checkTimes);
        }

        List<DailyCheckInfoResp.SituationCls> collect = schedulingAttendanceDailyApplyService.selectByDailyId(attendanceDailyDto.getId()).parallelStream().map(o -> {
            DailyCheckInfoResp.SituationCls situationCls = new DailyCheckInfoResp.SituationCls();
            if (SchedulingApplyConstants.ApplyTypeEnum.OUT.getType().equals(o.getApplyType())) {
                situationCls.setSituation(SchedulingApplyConstants.ApplyTypeEnum.OUT.getDesc());
                situationCls.setSituationType(SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.OUT.getType());
            } else if (SchedulingApplyConstants.ApplyTypeEnum.TRIP.getType().equals(o.getApplyType())) {
                situationCls.setSituation(SchedulingApplyConstants.ApplyTypeEnum.TRIP.getDesc());
                situationCls.setSituationType(SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.TRIP.getType());
            } else if (SchedulingApplyConstants.ApplyTypeEnum.APPLY_LEAVE.getType().equals(o.getApplyType())) {
                SchedulingApplyLeaveRecordDto schedulingApplyLeaveRecordDto = schedulingApplyLeaveRecordService.selectCacheById(o.getApplyId());
                if (Objects.isNull(schedulingApplyLeaveRecordDto)) {
                    return null;
                }
                String situation = SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE, schedulingApplyLeaveRecordDto.getLeaveType() + "");
                situationCls.setSituation(situation);
                situationCls.setSituationType(SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.VACATION.getType());
            } else {
                return null;
            }
            return situationCls;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        dailyCheckInfoResp.setSituations(collect);

        Map<String, Object> quertRecordMap = new HashMap<>();
        quertRecordMap.put(LambdaUtil.getFieldName(MongoOriginalCheckRecordDto::getDailyId), attendanceDailyDto.getId());
        List<DailyPreCheckResp.CheckTimeItemCls> checkRecord = mongoOriginalCheckRecordService.queryList(quertRecordMap)
                .parallelStream()
                .sorted((a, b) -> DateUtil.compare(a.getCheckTime(), b.getCheckTime()))
                .map(o -> {
                    DailyPreCheckResp.CheckTimeItemCls checkTimeItemCls = new DailyPreCheckResp.CheckTimeItemCls();
                    checkTimeItemCls.setCheckTime(o.getCheckTime());
                    checkTimeItemCls.setPics(o.getPics());
                    checkTimeItemCls.setRemark(o.getRemark());
                    checkTimeItemCls.setCheckAddress(o.getAddress());
                    checkTimeItemCls.setCheckIsField(o.getIsField());
                    return checkTimeItemCls;
                }).collect(Collectors.toList());
        dailyCheckInfoResp.setCheckRecords(checkRecord);
        return Response.<DailyCheckInfoResp>ok().wrap(dailyCheckInfoResp);
    }

    @GetMapping("calendar")
    @Operation(summary = "考勤日历")
    public Response<List<CalendarCheckInfoResp>> calendar(
            @Parameter(description = "月份 yyyy-MM", required = true) @RequestParam String month
    ){
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

        Map<String, Object> params = Request.parseParams(httpServletRequest);
        params.put(LambdaUtil.getFieldName(SchedulingAttendanceDailyDto::getPersonId), deptPersonDto.getId());

//        List<String> collect = schedulingAttendanceDailyService.queryList(params)
//                .parallelStream()
//                .filter(o -> !SchedulingAttendanceDailyConstants.SituationTypeEnum.WORK.getType().equals(o.getSituation()) && Objects.nonNull(o.getDate()))
//                .sorted((a, b) -> DateUtil.compare(a.getDate(), b.getDate()))
//                .map(o -> DateUtils.format(o.getDate(), DateUtils.FormatType.SIMPLE))
//                .collect(Collectors.toList());

        List<CalendarCheckInfoResp> collect = schedulingAttendanceDailyService.queryList(params).parallelStream()
                .filter(o -> Objects.nonNull(o.getDate()))
                .sorted((a, b) -> DateUtil.compare(a.getDate(), b.getDate()))
                .map(o -> {
                    CalendarCheckInfoResp calendarCheckInfoResp = new CalendarCheckInfoResp();
                    calendarCheckInfoResp.setDate(DateUtils.format(o.getDate(), DateUtils.FormatType.SIMPLE));
                    calendarCheckInfoResp.setCheckStatus(o.getCheckStatus());
                    calendarCheckInfoResp.setSituationType(o.getSituation());
                    SchedulingAttendanceDailyConstants.SituationTypeEnum byType = SchedulingAttendanceDailyConstants.SituationTypeEnum.getByType(o.getSituation());
                    assert byType != null;
                    calendarCheckInfoResp.setSituation(byType.getDesc());
                    return calendarCheckInfoResp;
                }).collect(Collectors.toList());
        return Response.<List<CalendarCheckInfoResp>>ok().wrap(collect);
    }


    @GetMapping("preCheck")
    @Operation(summary = "每日考勤前置查看")
    public Response<DailyPreCheckResp> preCheck(
            @Parameter(description = "经度", required = true) @RequestParam Double longitude,
            @Parameter(description = "纬度", required = true) @RequestParam Double latitude
    ) {
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

        final Date current = new Date();

        DailyPreCheckResp dailyPreCheckResp = new DailyPreCheckResp();
        String cache = RedisUtils.get(PERSON_ATTENDANCE_INFO_CACHE + deptPersonDto.getId());
        if (StringUtils.isNotBlank(cache)) {
            dailyPreCheckResp = JacksonUtils.parse(cache, DailyPreCheckResp.class);
        } else {
            dailyPreCheckResp.setPerson(deptPersonDto);
            SchedulingAttendanceGroupDto schedulingAttendanceGroupDto = schedulingAttendanceGroupPersonBindingService
                    .selectGroupByPersonId(deptPersonDto.getId());
            if (Objects.isNull(schedulingAttendanceGroupDto)) {
                return Response.error("未加入考勤组");
            }
            if (SystemConstants.DISABLE.equals(schedulingAttendanceGroupDto.getStatus())) {
                return Response.error("考勤组未启用");
            }
            dailyPreCheckResp.setAttendanceGroup(schedulingAttendanceGroupDto);
            List<DeptPersonDto> headers = schedulingAttendanceGroupHeadBindingService.selectPersonByGroupId(schedulingAttendanceGroupDto.getId());
            dailyPreCheckResp.setHeaders(headers);

            SchedulingAttendanceDailyDto attendanceDailyDto = schedulingAttendanceDailyService
                    .selectOne(deptPersonDto.getId(), DateUtils.format(current, DateUtils.FormatType.SIMPLE));
            if (Objects.isNull(attendanceDailyDto)) {
                // 产生今日考勤记录
                AttendanceDailyGenReq attendanceDailyGenReq = new AttendanceDailyGenReq();
                attendanceDailyGenReq.setPersonId(deptPersonDto.getId());
                attendanceDailyGenReq.setDataStr(DateUtils.format(current, DateUtils.FormatType.SIMPLE));
                attendanceDailyDto = schedulingAttendanceDailyService.gen(attendanceDailyGenReq);
            }
            dailyPreCheckResp.setChecks(Lists.newArrayList());
            if (Objects.nonNull(attendanceDailyDto) && CollectionUtils.isNotEmpty(attendanceDailyDto.getTimes())) {
                List<DailyPreCheckResp.CheckTimeCls> checkTimes = attendanceDailyDto.getTimes().parallelStream().map(o -> {
                    DailyPreCheckResp.CheckTimeCls checkTimeCls = new DailyPreCheckResp.CheckTimeCls();

                    checkTimeCls.setSchedulingName(o.getName());

                    DailyPreCheckResp.CheckTimeItemCls workItem = StringUtils.isNotBlank(o.getCheckWorkFieldJson())
                            ? JacksonUtils.parse(o.getCheckWorkFieldJson(), DailyPreCheckResp.CheckTimeItemCls.class)
                            : new DailyPreCheckResp.CheckTimeItemCls();
                    workItem.setTime(o.getWorkTime());
                    workItem.setCheckTime(o.getCheckWorkTime());
                    workItem.setCheckStatus(DateUtil.compare(current, o.getMissingWorkTime()) < 0
                            && SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(o.getCheckWorkStatus())
                            ? null : o.getCheckWorkStatus());
                    workItem.setCheckIsField(o.getCheckWorkIsField());
                    workItem.setCheckIsRepair(o.getCheckWorkIsRepair());
                    workItem.setCheckAddress(o.getCheckWorkAddress());
                    checkTimeCls.setWorkItem(workItem);

                    DailyPreCheckResp.CheckTimeItemCls closingItem = StringUtils.isNotBlank(o.getCheckClosingFieldJson())
                            ? JacksonUtils.parse(o.getCheckClosingFieldJson(), DailyPreCheckResp.CheckTimeItemCls.class)
                            : new DailyPreCheckResp.CheckTimeItemCls();
                    closingItem.setTime(o.getClosingTime());
                    closingItem.setCheckTime(o.getCheckClosingTime());
                    closingItem.setCheckStatus(DateUtil.compare(current, o.getEndSignTime()) < 0
                            && SchedulingAttendanceDailyConstants.TimeCheckStatusEnum.LACK.getStatus().equals(o.getCheckClosingStatus())
                            ? null : o.getCheckClosingStatus());
                    closingItem.setCheckIsField(o.getCheckClosingIsField());
                    closingItem.setCheckIsRepair(o.getCheckClosingIsRepair());
                    closingItem.setCheckAddress(o.getCheckClosingAddress());
                    checkTimeCls.setClosingItem(closingItem);

                    return checkTimeCls;
                }).sorted((a, b) -> DateUtil.compare(a.getWorkItem().getTime(), b.getWorkItem().getTime()))
                        .collect(Collectors.toList());
                dailyPreCheckResp.setChecks(checkTimes);
            }
            // 根据排班和申请判断得到的考勤状态
            checkSpecificSituation(deptPersonDto.getId(), current, dailyPreCheckResp);
            if (dailyPreCheckResp.getSituationType() == null) {
                // 没有申请，situation采用每日考勤记录上的
                assert attendanceDailyDto != null;
                SchedulingAttendanceDailyConstants.SituationTypeEnum byType = SchedulingAttendanceDailyConstants.SituationTypeEnum.getByType(attendanceDailyDto.getSituation());
                if (byType != null) {
                    dailyPreCheckResp.setSituationType(byType.getType());
                    dailyPreCheckResp.setSituation(byType.getDesc());
                }
            }

            // 考勤规则拼接
            descBuild(attendanceDailyDto, dailyPreCheckResp);
        }

        String dateStr = DateUtils.format(current, DateUtils.FormatType.SIMPLE);
        String week = DateUtil.dayOfWeekEnum(current).toChinese();
        dailyPreCheckResp.setDate(dateStr);
        dailyPreCheckResp.setWeekName(week);

        SchedulingAttendanceGroupDto attendanceGroup = dailyPreCheckResp.getAttendanceGroup();
        Preconditions.checkNotNull(attendanceGroup);
        double d = LocationUtils.getDistance(longitude, latitude, attendanceGroup.getLongitude(), attendanceGroup.getLatitude());
        dailyPreCheckResp.setIsField(d > attendanceGroup.getEffectiveRange()
                ? SchedulingAttendanceDailyConstants.IsFieldEnum.IS_FIELD.getType()
                : SchedulingAttendanceDailyConstants.IsFieldEnum.NOT_FIELD.getType());

        RedisUtils.set(PERSON_ATTENDANCE_INFO_CACHE + deptPersonDto.getId(), JacksonUtils.toJsonString(dailyPreCheckResp)
                , 2, TimeUnit.MINUTES);

        return Response.<DailyPreCheckResp>ok().wrap(dailyPreCheckResp);
    }

    /**
     * 考勤规则拼接
     *
     * @param dailyPreCheckResp
     */
    private void descBuild(SchedulingAttendanceDailyDto attendanceDailyDto, DailyPreCheckResp dailyPreCheckResp) {
        SchedulingAttendanceGroupDto attendanceGroup = dailyPreCheckResp.getAttendanceGroup();
        if (Objects.isNull(attendanceGroup)) {
            return;
        }

        DailyPreCheckResp.AttendanceRuleDescCls attendanceRuleDescCls = new DailyPreCheckResp.AttendanceRuleDescCls();

        if (CollectionUtils.isNotEmpty(attendanceDailyDto.getTimes())) {
            DailyPreCheckResp.AttendanceRuleDescChunk<?> attendanceRuleDescChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
            attendanceRuleDescChunk.setTitle("考勤时间");
            StringBuilder checkTimeDescContentBuilder = new StringBuilder();
            for (SchedulingAttendanceDailyTimeDto time : attendanceDailyDto.getTimes()) {
                checkTimeDescContentBuilder
                        .append(DateUtils.format(time.getWorkTime(), DateUtils.FormatType.TIME))
                        .append(" - ")
                        .append(DateUtils.format(time.getClosingTime(), DateUtils.FormatType.TIME))
                        .append("，")
                        .append("超过上班时间")
                        .append(DateUtil.between(time.getWorkTime(), time.getLateTime(), DateUnit.MINUTE, Boolean.TRUE))
                        .append("分钟打卡，记为迟到，早于下班时间")
                        .append(DateUtil.between(time.getEarlyLeaveTime(), time.getClosingTime(), DateUnit.MINUTE, Boolean.TRUE))
                        .append("分钟打卡，记为早退。")
                        .append("\n");
            }
            attendanceRuleDescChunk.setContent(checkTimeDescContentBuilder.toString());
            attendanceRuleDescCls.setCheckTimeDesc(attendanceRuleDescChunk);
        }

        DailyPreCheckResp.AttendanceRuleDescChunk<List<DailyPreCheckResp.AttendanceRuleDescChunk<?>>> workOvertimeDescChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
        workOvertimeDescChunk.setTitle("考勤规则");
        StringBuilder overtimeDescContentBuilder = new StringBuilder();
        if (SchedulingAttendanceGroupConstants.AllowReplacementEnum.NOT_ALLOW.getAllowType().equals(attendanceGroup.getAllowReplacement())) {
            overtimeDescContentBuilder.append("不");
        }
        overtimeDescContentBuilder.append("允许补卡，");
        if (SchedulingAttendanceGroupConstants.OutsideReplacementEnum.NOT_ALLOW.getAllowType().equals(attendanceGroup.getAllowOutsideReplacement())) {
            overtimeDescContentBuilder.append("不");
        }
        overtimeDescContentBuilder.append("允许外勤打卡，外勤");
        if (SchedulingAttendanceGroupConstants.OutsideNeedRemarkEnum.NOT_NEED.getNeedType().equals(attendanceGroup.getOutsideReplacementNeedRemark())) {
            overtimeDescContentBuilder.append("不");
        }
        overtimeDescContentBuilder.append("需要填写备注，外勤");
        if (SchedulingAttendanceGroupConstants.OutsideNeedImgsEnum.NOT_NEED.getNeedType().equals(attendanceGroup.getOutsideReplacementNeedImgs())) {
            overtimeDescContentBuilder.append("不");
        }
        overtimeDescContentBuilder.append("需要拍照。")
                .append("\n")
                .append("补卡次数：")
                .append(attendanceGroup.getReplacementLimitDay())
                .append("天内可补卡")
                .append(attendanceGroup.getReplacementLimitTimes())
                .append("次。\n");
        SchedulingWorkOvertimeConfigDto schedulingWorkOvertimeConfigDto = schedulingWorkOvertimeConfigService.selectCacheById(attendanceGroup.getWorkOvertimeId());
        if (Objects.nonNull(schedulingWorkOvertimeConfigDto) && SystemConstants.ENABLE.equals(schedulingWorkOvertimeConfigDto.getStatus())) {
            // 考勤规则扩展弹出列表拼接
            List<DailyPreCheckResp.AttendanceRuleDescChunk<?>> extendList = new ArrayList<>();

            DailyPreCheckResp.AttendanceRuleDescChunk<?> nameChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
            nameChunk.setTitle("规则名称");
            nameChunk.setContent("加班时长单位：" + (Objects.isNull(schedulingWorkOvertimeConfigDto.getUnit())
                    ? "-" : SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_WORK_OVERTIME_UNIT
                    , schedulingWorkOvertimeConfigDto.getUnit() + "")) + "。\n" + "取整方式：" + (Objects.isNull(schedulingWorkOvertimeConfigDto.getRoundingType())
                    ? "-" : SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_WORK_OVERTIME_ROUND_TYPE
                    , schedulingWorkOvertimeConfigDto.getRoundingType() + "")) + "。\n");
            extendList.add(nameChunk);

            if (StringUtils.isNotBlank(schedulingWorkOvertimeConfigDto.getWorkDayConfigJson())) {
                SchedulingWorkOvertimeConfigDto.WorkDayConfig workDayConfig = JacksonUtils.parse(schedulingWorkOvertimeConfigDto.getWorkDayConfigJson(), SchedulingWorkOvertimeConfigDto.WorkDayConfig.class);
                DailyPreCheckResp.AttendanceRuleDescChunk<?> weekdayChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
                weekdayChunk.setTitle("工作日加班");
                StringBuilder weekdayBuilder = new StringBuilder();
                if (workDayConfig.getStatus().equals(SchedulingWorkOvertimeConstants.WorkOvertimeAllowEnum.NOT_ALLOW.getAllowType())) {
                    weekdayBuilder.append("不允许工作日加班。\n");
                } else {
                    weekdayBuilder.append("允许工作日加班。\n");
                    weekdayBuilder.append("加班时长计算方式：")
                            .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.WORK_OVERTIME_TYPE, workDayConfig.getWorkOvertimeType() + ""))
                            .append("。\n");
                    weekdayBuilder.append("加班时长从下班")
                            .append(workDayConfig.getAfterWorkTime())
                            .append("分钟后起算。\n");
                    weekdayBuilder.append("结算方式：");
                    if (workDayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.RECORD.getMethodType())) {
                        weekdayBuilder.append("仅记录。\n");
                    } else if (workDayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.TRANSFER.getMethodType())) {
                        weekdayBuilder.append("按比例")
                                .append(workDayConfig.getRate())
                                .append("折算为")
                                .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE, workDayConfig.getDeductionHolidayType() + ""))
                                .append("。\n");
                    } else {
                        weekdayBuilder.append(" - 。\n");
                    }
                    if (workDayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.LIMIT.getLimitType())) {
                        weekdayBuilder.append("限制最小加班时长为")
                                .append(workDayConfig.getTodayLessTime())
                                .append("分钟。\n");
                    } else if (workDayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.NOT_LIMIT.getLimitType())) {
                        weekdayBuilder.append("不限制最小加班时长。\n");
                    }
                }
                weekdayChunk.setContent(weekdayBuilder.toString());
                extendList.add(weekdayChunk);
            }
            if (StringUtils.isNotBlank(schedulingWorkOvertimeConfigDto.getRestDayConfigJson())) {
                SchedulingWorkOvertimeConfigDto.RestDayConfig restDayConfig = JacksonUtils.parse(schedulingWorkOvertimeConfigDto.getRestDayConfigJson(), SchedulingWorkOvertimeConfigDto.RestDayConfig.class);
                DailyPreCheckResp.AttendanceRuleDescChunk<?> restDayChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
                restDayChunk.setTitle("休息日加班");
                StringBuilder restdayBuilder = new StringBuilder();
                if (restDayConfig.getStatus().equals(SchedulingWorkOvertimeConstants.WorkOvertimeAllowEnum.NOT_ALLOW.getAllowType())) {
                    restdayBuilder.append("不允许休息日加班。");
                } else {
                    restdayBuilder.append("允许休息日加班。\n");
                    restdayBuilder.append("加班时长计算方式：")
                            .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.WORK_OVERTIME_TYPE, restDayConfig.getWorkOvertimeType() + ""))
                            .append("。\n");
                    restdayBuilder.append("结算方式：");
                    if (restDayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.RECORD.getMethodType())) {
                        restdayBuilder.append("仅记录。\n");
                    } else if (restDayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.TRANSFER.getMethodType())) {
                        restdayBuilder.append("按比例")
                                .append(restDayConfig.getRate())
                                .append("折算为")
                                .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE, restDayConfig.getDeductionHolidayType() + ""))
                                .append("。\n");
                    } else {
                        restdayBuilder.append(" - 。\n");
                    }
                    if (restDayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.LIMIT.getLimitType())) {
                        restdayBuilder.append("限制最小加班时长为")
                                .append(restDayConfig.getTodayLessTime())
                                .append("分钟。\n");
                    } else if (restDayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.NOT_LIMIT.getLimitType())) {
                        restdayBuilder.append("不限制最小加班时长。\n");
                    }
                }
                restDayChunk.setContent(restdayBuilder.toString());
                extendList.add(restDayChunk);
            }
            if (StringUtils.isNotBlank(schedulingWorkOvertimeConfigDto.getHolidayConfigJson())) {
                SchedulingWorkOvertimeConfigDto.HolidayDayConfig holidayConfig = JacksonUtils.parse(schedulingWorkOvertimeConfigDto.getHolidayConfigJson(), SchedulingWorkOvertimeConfigDto.HolidayDayConfig.class);
                DailyPreCheckResp.AttendanceRuleDescChunk<?> holidayChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
                holidayChunk.setTitle("节假日加班");
                StringBuilder holidayBuilder = new StringBuilder();
                if (holidayConfig.getStatus().equals(SchedulingWorkOvertimeConstants.WorkOvertimeAllowEnum.NOT_ALLOW.getAllowType())) {
                    holidayBuilder.append("不允许节假日加班。");
                } else {
                    holidayBuilder.append("允许节假日加班。\n");
                    holidayBuilder.append("加班时长计算方式：")
                            .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.WORK_OVERTIME_TYPE, holidayConfig.getWorkOvertimeType() + ""))
                            .append("。\n");
                    holidayBuilder.append("结算方式：");
                    if (holidayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.RECORD.getMethodType())) {
                        holidayBuilder.append("仅记录。\n");
                    } else if (holidayConfig.getSettlementType().equals(SchedulingWorkOvertimeConstants.SettlementMethodEnum.TRANSFER.getMethodType())) {
                        holidayBuilder.append("按比例")
                                .append(holidayConfig.getRate())
                                .append("折算为")
                                .append(SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE, holidayConfig.getDeductionHolidayType() + ""))
                                .append("。\n");
                    } else {
                        holidayBuilder.append(" - 。\n");
                    }
                    if (holidayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.LIMIT.getLimitType())) {
                        holidayBuilder.append("限制最小加班时长为")
                                .append(holidayConfig.getTodayLessTime())
                                .append("分钟。\n");
                    } else if (holidayConfig.getMinimumOvertimeType().equals(SchedulingWorkOvertimeConstants.LimitTimeEnum.NOT_LIMIT.getLimitType())) {
                        holidayBuilder.append("不限制最小加班时长。\n");
                    }
                }
                holidayChunk.setContent(holidayBuilder.toString());
                extendList.add(holidayChunk);
            }

            workOvertimeDescChunk.setExtend(extendList);
        }
        workOvertimeDescChunk.setContent(overtimeDescContentBuilder.toString());
        attendanceRuleDescCls.setOvertimeDesc(workOvertimeDescChunk);

        DailyPreCheckResp.AttendanceRuleDescChunk<?> groupDescChunk = new DailyPreCheckResp.AttendanceRuleDescChunk<>();
        groupDescChunk.setTitle("考勤组管理");
        StringBuilder groupDescContentBuilder = new StringBuilder();
        groupDescContentBuilder.append("有效打卡范围：")
                .append(attendanceGroup.getSignAddress())
                .append(attendanceGroup.getEffectiveRange())
                .append("米以内。")
                .append("\n");
        if (CollectionUtils.isNotEmpty(dailyPreCheckResp.getHeaders())) {
            groupDescContentBuilder.append("考勤组负责人：");
            for (DeptPersonDto header : dailyPreCheckResp.getHeaders()) {
                groupDescContentBuilder.append(header.getName()).append("，");
            }
            groupDescContentBuilder.setCharAt(groupDescContentBuilder.length() - 1, '。');
            groupDescContentBuilder.append("\n");
        }
        groupDescChunk.setContent(groupDescContentBuilder.toString());
        attendanceRuleDescCls.setGroupDesc(groupDescChunk);

        dailyPreCheckResp.setAttendanceRuleDesc(attendanceRuleDescCls);
    }

    /**
     * 查看人员某时间点的准确的考勤状态，优先级：假期>出差>外出>班（休）
     * 每日考勤记录上有situation标记了班休假，这里查看时间点上是否有请假或者其他的申请
     * @param personId
     * @param date
     * @return
     */
    private void checkSpecificSituation(Long personId, Date date, DailyPreCheckResp dailyPreCheckResp) {
//        List<SchedulingTimeConfigDto> times = schedulingAttendanceGroupPersonWorkTimeConfigService.workTime(personId, DateUtils.format(date, DateUtils.FormatType.SIMPLE));
//        String situation = CollectionUtils.isNotEmpty(times) ? SchedulingAttendanceDailyConstants.CHECK_SITUATIONS[0] : SchedulingAttendanceDailyConstants.CHECK_SITUATIONS[1];
//        Byte situationType = CollectionUtils.isNotEmpty(times) ? SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.WORK.getType()
//                : SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.REST.getType();
        String situation = null;
        Byte situationType = null;
        String[] timeSlot = null;

        List<SchedulingApplyLeaveRecordDto> leaveRecordDtos = schedulingApplyLeaveRecordService.selectByPersonId(personId, date);
        if (CollectionUtils.isNotEmpty(leaveRecordDtos)) {
            Map<String, Object> queryResumptionMap = new HashMap<>();
            for (SchedulingApplyLeaveRecordDto schedulingApplyLeaveRecordDto : leaveRecordDtos) {
                queryResumptionMap.put(LambdaUtil.getFieldName(SchedulingApplyResumptionLeaveRecordDto::getOriginalRecordId), schedulingApplyLeaveRecordDto.getId());
                List<SchedulingApplyResumptionLeaveRecordDto> schedulingApplyResumptionLeaveRecordDtos = schedulingApplyResumptionLeaveRecordService.queryList(queryResumptionMap);
                if (CollectionUtils.isEmpty(schedulingApplyResumptionLeaveRecordDtos)) {
                    situation = SysCodeUtils.easyToGetName(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE, schedulingApplyLeaveRecordDto.getLeaveType() + "");
                    timeSlot = new String[]{DateUtils.format(schedulingApplyLeaveRecordDto.getStartTime(), DateUtils.FormatType.COMMON)
                            , DateUtils.format(schedulingApplyLeaveRecordDto.getEndTime(), DateUtils.FormatType.COMMON)};
                    situationType = SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.VACATION.getType();
                    break;
                }
            }
        } else {
            List<SchedulingApplyOutRecordDto> outRecordDtos = schedulingApplyOutRecordService.selectByPersonId(personId, date);
            if (CollectionUtils.isNotEmpty(outRecordDtos)) {
                situation = SchedulingApplyConstants.ApplyTypeEnum.OUT.getDesc();
                SchedulingApplyOutRecordDto schedulingApplyOutRecordDto = outRecordDtos.get(0);
                timeSlot = new String[]{DateUtils.format(schedulingApplyOutRecordDto.getStartTime(), DateUtils.FormatType.COMMON)
                        , DateUtils.format(schedulingApplyOutRecordDto.getEndTime(), DateUtils.FormatType.COMMON)};
                situationType = SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.OUT.getType();
            }
            List<SchedulingApplyTripRecordDto> tripRecordDtos = schedulingApplyTripRecordService.selectByPersonId(personId, date);
            if (CollectionUtils.isNotEmpty(tripRecordDtos)) {
                situation = SchedulingApplyConstants.ApplyTypeEnum.TRIP.getDesc();
                SchedulingApplyTripRecordDto schedulingApplyTripRecordDto = tripRecordDtos.get(0);
                timeSlot = new String[]{DateUtils.format(schedulingApplyTripRecordDto.getStartTime(), DateUtils.FormatType.COMMON)
                        , DateUtils.format(schedulingApplyTripRecordDto.getEndTime(), DateUtils.FormatType.COMMON)};
                situationType = SchedulingAttendanceDailyConstants.MobileSituationTypeEnum.TRIP.getType();
            }
        }

        dailyPreCheckResp.setSituationType(situationType);
        dailyPreCheckResp.setSituation(situation);
        dailyPreCheckResp.setSituationTimeSlot(timeSlot);
    }

//    private double calculateDistance(double lon1, double lat1, double lon2, double lat2) {
//        final double earthRadius = 6371;
//
//        double radiansLat1 = Math.toRadians(lat1);
//        double radiansLon1 = Math.toRadians(lon1);
//        double radiansLat2 = Math.toRadians(lat2);
//        double radiansLon2 = Math.toRadians(lon2);
//
//        double dLat = radiansLat2 - radiansLat1;
//        double dLon = radiansLon2 - radiansLon1;
//
//        double a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(radiansLat1) * Math.cos(radiansLat2)
//                * Math.pow(Math.sin(dLon / 2), 2);
//        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
//
//        return earthRadius * c * 1000;
//    }

    @PostMapping("check")
    @Operation(summary = "考勤")
    public Response<DailyCheckRespDto> check(
            @RequestBody @Validated DailyCheckReq dailyCheckReq
    ) {
        SessionHolder.checkEmp();
        DeptPersonDto deptPersonDto = Objects.requireNonNull(SessionHolder.getLoginEmpSession()).getDeptPersonDto();

//        final Date current = new Date();

        SchedulingAttendanceGroupDto attendanceGroup = schedulingAttendanceGroupPersonBindingService
                .selectGroupByPersonId(deptPersonDto.getId());
        Preconditions.checkNotNull(attendanceGroup);

        double d = LocationUtils.getDistance(dailyCheckReq.getLongitude(), dailyCheckReq.getLatitude(), attendanceGroup.getLongitude(), attendanceGroup.getLatitude());
        if (d > attendanceGroup.getEffectiveRange()
                && SchedulingAttendanceGroupConstants.OutsideReplacementEnum.NOT_ALLOW.getAllowType().equals(attendanceGroup.getAllowOutsideReplacement())) {
            return Response.error("不允许外勤打卡");
        }
        if (d > attendanceGroup.getEffectiveRange()
                && SchedulingAttendanceGroupConstants.OutsideNeedImgsEnum.NEED.getNeedType().equals(attendanceGroup.getOutsideReplacementNeedImgs())
                && StringUtils.isBlank(dailyCheckReq.getPics())) {
            return Response.error("外勤打卡需要上传照片，请重新尝试");
        }
        if (d > attendanceGroup.getEffectiveRange()
                && SchedulingAttendanceGroupConstants.OutsideNeedRemarkEnum.NEED.getNeedType().equals(attendanceGroup.getOutsideReplacementNeedRemark())
                && StringUtils.isBlank(dailyCheckReq.getRemark())) {
            return Response.error("外勤打卡需要上传备注，请重新尝试");
        }

        Byte isField = d > attendanceGroup.getEffectiveRange()
                ? SchedulingAttendanceDailyConstants.IsFieldEnum.IS_FIELD.getType()
                : SchedulingAttendanceDailyConstants.IsFieldEnum.NOT_FIELD.getType();

        DailyCheckReqDto dailyCheckReqDto = CopyUtils.copyObj(dailyCheckReq, DailyCheckReqDto.class);
        dailyCheckReqDto.setPersonId(deptPersonDto.getId());
        dailyCheckReqDto.setIsField(isField);
        dailyCheckReqDto.setIsRepair(SchedulingAttendanceDailyConstants.IsRepairEnum.NOT_REPAIR.getType());
        dailyCheckReqDto.setCheckDate(null);

        DailyCheckRespDto dailyCheckRespDto = schedulingAttendanceDailyService.check(dailyCheckReqDto);
        if (Boolean.TRUE.equals(dailyCheckRespDto.getSuccess())) {
            RedisUtils.del(PERSON_ATTENDANCE_INFO_CACHE + deptPersonDto.getId());
        }
        return Response.<DailyCheckRespDto>ok().wrap(dailyCheckRespDto);
    }
}
